package com.czstudio.ble_tools;

import androidx.appcompat.app.AppCompatActivity;

import android.Manifest;
import android.bluetooth.BluetoothGattDescriptor;
import android.os.Bundle;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "BLEExample";

    private static MainActivity instance;
    private static final int REQUEST_ENABLE_BT = 1;
    private static final int LOCATION_PERMISSION_REQUEST_CODE = 2;

    private BluetoothAdapter bluetoothAdapter;
    private BluetoothLeScanner bluetoothLeScanner;
    private List<BluetoothDevice> devices;
    private ArrayAdapter<String> deviceAdapter;
    private BluetoothGatt bluetoothGatt;

    private static final int BLUETOOTH_PERMISSION_REQUEST_CODE = 1;
    static final String SERVICE_UUID = "55535343-fe7d-4ae5-8fa9-9fafd205e455";
    static final String CHARACTISTIC_TX_UUID = "49535343-8841-43f4-a8d4-ecbe34729bb3";
    static final String CHARACTISTIC_RX_UUID = "49535343-1e4d-4bd9-ba61-23c647249616";
    private static final String CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID = "00002902-0000-1000-8000-00805f9b34fb";
    int selectDeviceIdx = 0;

    long lastCharacteristicChangedStamp=0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        instance = this;
        // Initialize UI components
        Button btnScan = findViewById(R.id.btn_scan);
        ListView lvDevices = findViewById(R.id.lv_devices);
        Button btnConnect = findViewById(R.id.btn_connect);
        Button btnRead = findViewById(R.id.btn_read);
        Button btnWrite = findViewById(R.id.btn_write);

        //request permission
        requestSysPermissions();

        // Initialize Bluetooth
        final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        bluetoothAdapter = bluetoothManager.getAdapter();
        devices = new ArrayList<>();
        deviceAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1);
        lvDevices.setAdapter(deviceAdapter);

        // Check Bluetooth support
        if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }

        // Check location permission
        if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
        }

        // Set up button click listeners
        btnScan.setOnClickListener(v -> startScanning());
        btnConnect.setOnClickListener(v -> connectToDevice());
        btnRead.setOnClickListener(v -> readCharacteristic());
        btnWrite.setOnClickListener(v -> writeCharacteristic());

        lvDevices.setOnItemClickListener((parent, view, position, id) -> {
            selectDeviceIdx = position;
//            Toast.makeText(MainActivity.this, "Selected: " + devices.get(position).getName(), Toast.LENGTH_SHORT).show();
        });
    }

    String permissionArray[] = {Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT};

    private void requestSysPermissions() {
        for (int i = 0; i < permissionArray.length; i++) {
            if (ContextCompat.checkSelfPermission(this, permissionArray[i])
                    != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this,
                        permissionArray,
                        BLUETOOTH_PERMISSION_REQUEST_CODE);
            } else {
                Toast.makeText(this, "Bluetooth permissions already granted", Toast.LENGTH_SHORT).show();
            }
        }
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN)
                != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT)
                        != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT},
                    BLUETOOTH_PERMISSION_REQUEST_CODE);
        } else {
            Toast.makeText(this, "Bluetooth permissions already granted", Toast.LENGTH_SHORT).show();
        }
    }


    private void startScanning() {
        devices.clear();
        deviceAdapter.clear();

        if (bluetoothAdapter != null && bluetoothAdapter.isEnabled()) {
            bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
                // TODO: Consider calling
                //    ActivityCompat#requestPermissions
                // here to request the missing permissions, and then overriding
                //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                //                                          int[] grantResults)
                // to handle the case where the user grants the permission. See the documentation
                // for ActivityCompat#requestPermissions for more details.
                ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
                Toast.makeText(instance, "权限 Manifest.permission.BLUETOOTH_SCAN 失败", Toast.LENGTH_LONG).show();
                return;
            }
            bluetoothLeScanner.startScan(new ScanCallback() {
                @Override
                public void onScanResult(int callbackType, ScanResult result) {
                    BluetoothDevice device = result.getDevice();
                    if (!devices.contains(device)) {
                        devices.add(device);
                        if (ActivityCompat.checkSelfPermission(instance, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                            // TODO: Consider calling
                            //    ActivityCompat#requestPermissions
                            // here to request the missing permissions, and then overriding
                            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                            //                                          int[] grantResults)
                            // to handle the case where the user grants the permission. See the documentation
                            // for ActivityCompat#requestPermissions for more details.
                            return;
                        }
                        deviceAdapter.add(device.getName() + "\n" + device.getAddress());
                    }
                }

                @Override
                public void onScanFailed(int errorCode) {
                    Log.e(TAG, "Scan failed with error code: " + errorCode);
                }
            });
        } else {
            Toast.makeText(this, "Bluetooth is not enabled", Toast.LENGTH_SHORT).show();
        }
    }

    private void stopScanning() {
        if (bluetoothLeScanner != null) {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
                // TODO: Consider calling
                //    ActivityCompat#requestPermissions
                // here to request the missing permissions, and then overriding
                //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                //                                          int[] grantResults)
                // to handle the case where the user grants the permission. See the documentation
                // for ActivityCompat#requestPermissions for more details.
                return;
            }
            bluetoothLeScanner.stopScan((ScanCallback) null);
        }
    }

    private void connectToDevice() {
        if (devices.size() > 0) {
//            BluetoothDevice device = devices.get(0); // Connect to the first device in the list

            BluetoothDevice device = devices.get(selectDeviceIdx);
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                // TODO: Consider calling
                //    ActivityCompat#requestPermissions
                // here to request the missing permissions, and then overriding
                //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                //                                          int[] grantResults)
                // to handle the case where the user grants the permission. See the documentation
                // for ActivityCompat#requestPermissions for more details.
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.BLUETOOTH_CONNECT}, LOCATION_PERMISSION_REQUEST_CODE);
                Toast.makeText(instance, "权限 BLUETOOTH_CONNECT 失败", Toast.LENGTH_LONG).show();
                return;
            } else {
                Toast.makeText(instance, "权限 BLUETOOTH_CONNECT 成功", Toast.LENGTH_LONG).show();
            }
            Toast.makeText(MainActivity.this, "Selected: " + devices.get(selectDeviceIdx).getName(), Toast.LENGTH_SHORT).show();
            bluetoothGatt = device.connectGatt(this, false, gattCallback);
            if (bluetoothGatt != null) {
                Log.e("BLE", "bluetoothGat!=null");
                if (bluetoothGatt.connect()) {
                    Log.e("BLE", "bluetoothGat.connect 成功");
                } else {
                    Log.e("BLE", "bluetoothGat.connect 失败");
                }
            } else {
                Toast.makeText(instance, "连接失败", Toast.LENGTH_LONG).show();
            }
        } else {
            Toast.makeText(this, "No device selected", Toast.LENGTH_SHORT).show();
        }
    }

    private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            if (newState == BluetoothGatt.STATE_CONNECTED) {
                Log.i(TAG, "Connected to GATT server");
                if (ActivityCompat.checkSelfPermission(instance, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                    // TODO: Consider calling
                    //    ActivityCompat#requestPermissions
                    // here to request the missing permissions, and then overriding
                    //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                    //                                          int[] grantResults)
                    // to handle the case where the user grants the permission. See the documentation
                    // for ActivityCompat#requestPermissions for more details.
                    ActivityCompat.requestPermissions(instance, new String[]{Manifest.permission.BLUETOOTH_CONNECT}, LOCATION_PERMISSION_REQUEST_CODE);
                    return;
                }
                gatt.discoverServices();
            } else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
                Log.i(TAG, "Disconnected from GATT server");
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                displayGattServices(gatt.getServices());
                BluetoothGattService service = gatt.getService(UUID.fromString(SERVICE_UUID));
                if (service != null) {
                    BluetoothGattCharacteristic characteristicRx = service.getCharacteristic(UUID.fromString(CHARACTISTIC_RX_UUID));
                    if (characteristicRx != null) {

                        sendMsg(gatt, characteristicRx, "HELLO, BLE!");
                    }
                    BluetoothGattCharacteristic characteristicTx = service.getCharacteristic(UUID.fromString(CHARACTISTIC_TX_UUID));
                    if (characteristicTx != null) {
                        enableNotifications(gatt, characteristicTx);
                    }

                }
            } else {
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            long stampMs = System.currentTimeMillis();
            if(stampMs -lastCharacteristicChangedStamp<10){
                Log.i("Bluetooth", "onCharacteristicChanged callback too fast ");
                return;
            }
            lastCharacteristicChangedStamp = stampMs;

            // 处理接收到的消息
            String message = new String(characteristic.getValue(), Charset.forName("UTF-8"));
            Log.i("Bluetooth", "Received message: " + message);
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.i(TAG, "Characteristic read: " + new String(characteristic.getValue(), Charset.forName("UTF-8")));
            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.i(TAG, "Characteristic written: " +  new String(characteristic.getValue(), Charset.forName("UTF-8")));
            }
        }
    };

    private void displayGattServices(List<BluetoothGattService> gattServices) {
        if (gattServices == null) return;

        for (BluetoothGattService gattService : gattServices) {
            List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
            for (BluetoothGattCharacteristic characteristic : gattCharacteristics) {
                Log.i(TAG, "Service: " + gattService.getUuid() + ", Characteristic: " + characteristic.getUuid());
            }
        }
    }

    private void readCharacteristic() {
        if (bluetoothGatt != null) {
            BluetoothGattService service = bluetoothGatt.getService(UUID.fromString(SERVICE_UUID)); // Example service UUID
            if (service != null) {
                BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(CHARACTISTIC_RX_UUID)); // Example characteristic UUID
                if (characteristic != null) {
                    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                        // TODO: Consider calling
                        //    ActivityCompat#requestPermissions
                        // here to request the missing permissions, and then overriding
                        //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                        //                                          int[] grantResults)
                        // to handle the case where the user grants the permission. See the documentation
                        // for ActivityCompat#requestPermissions for more details.
                        return;
                    }
                    bluetoothGatt.readCharacteristic(characteristic);
                }
            }
        }
    }

    private void writeCharacteristic() {
        if (bluetoothGatt != null) {
            BluetoothGattService service = bluetoothGatt.getService(UUID.fromString(SERVICE_UUID)); // Example service UUID
            if (service != null) {
                BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(CHARACTISTIC_RX_UUID)); // Example characteristic UUID
                if (characteristic != null) {
                    characteristic.setValue(new byte[]{0x01});
                    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                        // TODO: Consider calling
                        //    ActivityCompat#requestPermissions
                        // here to request the missing permissions, and then overriding
                        //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                        //                                          int[] grantResults)
                        // to handle the case where the user grants the permission. See the documentation
                        // for ActivityCompat#requestPermissions for more details.
                        return;
                    }
                    bluetoothGatt.writeCharacteristic(characteristic);
                }
            }
        }
    }

    private void sendMsg(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, String message) {
        byte[] value = message.getBytes(Charset.forName("UTF-8"));
        characteristic.setValue(value);
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        boolean status = gatt.writeCharacteristic(characteristic);
        if (status) {
            Log.i("Bluetooth", "Message sent successfully.");
        } else {
            Log.e("Bluetooth", "Failed to send message.");
        }
    }

    private void enableNotifications(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        if (!gatt.setCharacteristicNotification(characteristic, true)) {
            Log.e("Bluetooth", "Failed to set characteristic notification.");
            return;
        }

        // 获取客户端特性配置描述符
        BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID));
        if (descriptor != null) {
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            gatt.writeDescriptor(descriptor);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (bluetoothGatt != null) {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                // TODO: Consider calling
                //    ActivityCompat#requestPermissions
                // here to request the missing permissions, and then overriding
                //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                //                                          int[] grantResults)
                // to handle the case where the user grants the permission. See the documentation
                // for ActivityCompat#requestPermissions for more details.
                return;
            }
            bluetoothGatt.close();
            bluetoothGatt = null;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission granted, start scanning
                startScanning();
            } else {
                Toast.makeText(this, "Location permission denied", Toast.LENGTH_SHORT).show();
            }
        }
    }
}